package c.x.jy.service.utils.wechat;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.spec.AlgorithmParameterSpec;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import c.x.jy.common.exception.RRException;
import c.x.jy.common.utils.Constant;
import c.x.jy.service.redis.RedisUtils;
import lombok.extern.slf4j.Slf4j;

/**
 * 微信API Created by banban on 2018/6/5
 */
@Slf4j
@SuppressWarnings("all")
public class PayCommonUtil {

	public static void main(String[] args) throws Exception {
		// getXcxCode("666");
//        getminiqrQr();
	}

	// 支付类型
	public enum TradeType {
		JSAPI("JSAPI"), NATIVE("NATIVE"), APP("APP");

		private String value;

		public String getValue() {
			return value;
		}

		public void setValue(String value) {
			this.value = value;
		}

		TradeType(String value) {
			this.value = value;
		}
	}

	/**
	 * 统一下单接口
	 *
	 * @param orderNo        订单号
	 * @param amount         金额,转化成分
	 * @param description    产品描述
	 * @param spbillCreateIP 客户端IP
	 * @param openid
	 * @return 统一下单接口 微信返回订单prepay_id 默认tradeType JSAPI
	 */
	public static Map<String, Object> wechatPrePay(String orderNo, BigDecimal amount, String description, 	String spbillCreateIP, String openid, WechatConfig wechatConfig)   {
		return wechatPrePay(orderNo, amount, TradeType.JSAPI, description, spbillCreateIP, Constant.NOTIFY_URL, openid, wechatConfig);
	}

	/**
	 * 统一下单接口
	 *
	 * @param orderNo        订单号
	 * @param amount         金额,转化成分
	 * @param tradeType      支付类型
	 * @param description    产品描述
	 * @param spbillCreateIP 客户端IP
	 * @param openid
	 * @return 统一下单接口 微信返回订单prepay_id
	 */
	public static Map<String, Object> wechatPrePay(String orderNo, BigDecimal amount, TradeType tradeType,
			String description, String spbillCreateIP, String notifyUrl, String openid, WechatConfig wechatConfig) {
		SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
		parameterMap.put("appid", wechatConfig.getGzhAppId()); // 小程序ID
		parameterMap.put("mch_id", wechatConfig.getMchId());// 商户号
		parameterMap.put("nonce_str", WechatCommonUtil.createNonceStr(32));// 随机字符串
		parameterMap.put("body", description); // 商品描述
		parameterMap.put("out_trade_no", orderNo); // 订单号
		parameterMap.put("fee_type", "CNY"); // 币种 CNY:人民币
		BigDecimal total = amount.setScale(2, BigDecimal.ROUND_HALF_DOWN);
		total = total.multiply(new BigDecimal(100));
		DecimalFormat df = new DecimalFormat("0");
		parameterMap.put("total_fee", df.format(total)); // 金额
		// parameterMap.put("time_expire", df.format(total));

		// parameterMap.put("total_fee", "1");//测试金额
		parameterMap.put("spbill_create_ip", spbillCreateIP);// 终端IP
		parameterMap.put("notify_url", notifyUrl);// 通知地址
		parameterMap.put("trade_type", tradeType.getValue());
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");

//      设置失效时间
		// Date date = new Date();
		// parameterMap.put("time_start", sdf.format(date));
		// Calendar calendar = Calendar.getInstance();
		// calendar.setTime(date);
		// calendar.add(Calendar.MINUTE, 3);
		parameterMap.put("time_expire", sdf.format(new Date().getTime() + (2.5 * 60 * 1000)));

		if (tradeType.getValue().equals(TradeType.JSAPI.getValue())) {
			parameterMap.put("openid", openid);
		}

		String sign = createSign(parameterMap, wechatConfig.getApiKey());// ApiKey 支付密钥
		parameterMap.put("sign", sign);
		String requestXML = WechatCommonUtil.getRequestXml(parameterMap);
		log.info("统一下单参数:" + requestXML);
		// System.out.print("统一下单参数:" + requestXML);
		String result = WechatCommonUtil.httpsRequest(Constant.UNIFIED_ORDER_URL, "POST", requestXML);
		log.info("统一下单返回prepay_id:" + result);
		// logger.error("统一下单返回prepay_id:" + result);
		// System.out.print("统一下单返回prepay_id:" + result);
		Map<String, Object> map = new HashMap<String, Object>();
	
 
			map = WechatCommonUtil.getMapFromXML(result);
			log.info("统一下单返回prepay_id:" + map);
	 
		
		return payData(map,wechatConfig);
	}
	
	/**
	 * {nonce_str=I8T5leEhVdsr1z1D, appid=wxfa4c7da781e3bc1f, sign=1720E2C4933A43F8013413DF0C2F5643, trade_type=JSAPI, return_msg=OK, result_code=SUCCESS, mch_id=1481967332, 
	 * return_code=SUCCESS, prepay_id=wx1708590946797404b677c2c63df1d30000}
	 * @param map
	 * @return
	 */
	private static Map payData(Map map,WechatConfig config ) {
		
		//  log.debug("支付成功，总金额{ } " );
	        log.info("支付回调数据: {}",map.toString());
	        if(!map.get("return_code").equals("SUCCESS")) {
	            log.error("支付失败：回调信息{}", map.toString());
	            throw new RRException(  "支付失败");
	        } 
	        SortedMap<String,Object> tempMap = new TreeMap<String, Object>();
	        tempMap.put("appId",map.get("appid"));
	        tempMap.put("timeStamp", (System.currentTimeMillis()/1000)+"");
	        tempMap.put("nonceStr",WechatCommonUtil.createNonceStr(32));
	        tempMap.put("package","prepay_id="+map.get("prepay_id"));
	        tempMap.put("signType","MD5");
	        String paySign = PayCommonUtil.createSign(tempMap, config.getApiKey());
	        tempMap.put("paySign",paySign);
	
	        return tempMap;
	}

	/**
	 * 企业向用户付款接口
	 *
	 * @param tradeNo
	 * @param openId
	 * @param money
	 * @param desc
	 * @param ip
	 * @param wechatConfig
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> wechatPayMember(String tradeNo, String openId, BigDecimal money, String desc,
			String ip, WechatConfig wechatConfig) throws Exception {
		SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
		parameterMap.put("mch_appid", wechatConfig.getAppId());
		parameterMap.put("mchid", wechatConfig.getMchId());
		parameterMap.put("nonce_str", WechatCommonUtil.createNonceStr(32));
		parameterMap.put("partner_trade_no", tradeNo);
		parameterMap.put("openid", openId);
		parameterMap.put("check_name", "NO_CHECK");
		BigDecimal amount = money.setScale(2, BigDecimal.ROUND_HALF_DOWN);
		amount = amount.multiply(new BigDecimal(100));
		DecimalFormat df = new DecimalFormat("0");
		parameterMap.put("amount", df.format(amount));
		parameterMap.put("desc", desc);
		parameterMap.put("spbill_create_ip", ip);
		String sign = createSign(parameterMap, wechatConfig.getApiKey());// ApiKey 支付密钥
		parameterMap.put("sign", sign);
		String requestXML = WechatCommonUtil.getRequestXml(parameterMap);
		String result = WechatCommonUtil.sendSSLPost(Constant.PAY_USER_URL, requestXML, wechatConfig.getMchId(),
				wechatConfig.getCaPath() + "/" + wechatConfig.getMchId() + "/");
		log.debug(result);
		Map<String, Object> map = new HashMap<String, Object>();
		map = WechatCommonUtil.getMapFromXML(result);
		return map;
	}

	/**
	 * 统一下单接口签名
	 *
	 * @param parameters
	 * @return 排序map生成签名
	 */
	public static String createSign(SortedMap<String, Object> parameters, String apiKey) {
		ArrayList<String> list = new ArrayList<String>();
		for (Map.Entry<String, Object> entry : parameters.entrySet()) {
			if (entry.getValue() != "") {
				list.add(entry.getKey() + "=" + entry.getValue() + "&");
			}
		}
		int size = list.size();
		String[] arrayToSort = list.toArray(new String[size]);
		Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < size; i++) {
			sb.append(arrayToSort[i]);
		}
		String result = sb.toString();
		result += "key=" + apiKey;
		result = MD5Util.MD5Encode(result).toUpperCase();
		return result;
	}

	/**
	 * 前端支付接口签名
	 *
	 * @param parameters
	 * @return
	 * @throws Exception
	 */
	public static String createPaySign(SortedMap<String, Object> parameters) throws Exception {
		ArrayList<String> list = new ArrayList<String>();
		for (Map.Entry<String, Object> entry : parameters.entrySet()) {
			if (entry.getValue() != "") {
				list.add(entry.getKey() + "=" + entry.getValue() + "&");
			}
		}
		String last = list.get(list.size() - 1);
		last = last.substring(0, last.length() - 1);
		list.remove(list.size() - 1);
		list.add(last);
		int size = list.size();
		String[] arrayToSort = list.toArray(new String[size]);
		Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < size; i++) {
			sb.append(arrayToSort[i]);
		}
		String result = sb.toString();
		result = MD5Util.MD5Encode(result).toUpperCase();
		return result;
	}

	public static Map<String, Object> queryOrder(WechatConfig wechatConfig, String transactionId) throws Exception {
		SortedMap<String, Object> map = new TreeMap();
		map.put("appid", wechatConfig.getAppId());
		map.put("mch_id", wechatConfig.getMchId());
		map.put("transaction_id", transactionId);
		map.put("nonce_str", WechatCommonUtil.createNonceStr(32));
		map.put("sign", createSign(map, wechatConfig.getApiKey()));
		String requestXML = WechatCommonUtil.getRequestXml(map);
		String result = WechatCommonUtil.sendPost(Constant.ORDER_QUERY_URL, requestXML);
		log.debug(result);
		Map<String, Object> resultMap = new HashMap<String, Object>();
		resultMap = WechatCommonUtil.getMapFromXML(result);
		return resultMap;
	}

	/**
	 * 获取openid 参数只能拼在URL上
	 *
	 * @param code 小程序login返回code
	 * @return {"session_key":"1+9u3GXla06hHAmTOukw==","openid":"oDdx_4mBdXLr5GjI07-mtZKrM"}
	 *         {"session_key":"RMc81VBZ6QtPjtGztw9PaA==","openid":"oDdx_4mBdXLr5GjI07-mSctoZKrM"}
	 * @throws Exception
	 */
	public static String getOpenId(String code, WechatConfig config) throws Exception {
		String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + config.getAppId() + "&secret="
				+ config.getAppSecret() + "&js_code=" + code + "&grant_type=authorization_code";
		String result = WechatCommonUtil.httpsRequest(url, "POST", null);
		System.out.print("获取1openid:" + result);

		if (result.indexOf("errcode") > 0) {
			return "errro";// 报错
		}
		return result;
	}

	/**
	 * 获取Access_token CacheNames
	 */
	private static String getAccessToken(WechatConfig config) {
		String token = null;

		Object ot = RedisUtils.redisUtils.get(config.getAppId());
		// if(CacheReBackMsg.get(CacheReBackMsg.ACCESS_TOKEN, config.getAppId())==null){
		if (ot == null) {
			
			String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
					+ config.getAppId() + "&secret=" + config.getAppSecret();
			String result = WechatCommonUtil.httpsRequest(url, "GET", null);
			log.info("获取AccessToken:" + result);
			// System.out.println("获取AccessToken:" + result);
			// {"access_token":"9_9p8sPVtlzJMkAbASgpkKF4GpNqKp4fCAz7F2EBgUTFF64QYmJF5unPxklQ-a38Y5najyU22HpFighg_sPHO1Utn1WqrI6q5gz5rKFkqJqJmIPVcFyteuLRgLczPYkeDoTB3M-hBtHXJ5FBLBJBQeABAXPE","expires_in":7200}
			if (result.indexOf("access_token") < 0) {
				return null;
			}
			Map map = (Map) JSON.parse(result);
		 
			token = map.get("access_token").toString();
			RedisUtils.redisUtils.set(config.getAppId(), token, 7000l);
			// CacheReBackMsg.put(CacheReBackMsg.ACCESS_TOKEN, config.getAppId(), token);
		} else {
			// token= CacheReBackMsg.get(CacheReBackMsg.ACCESS_TOKEN, config.getAppId());
			token = ot.toString();
		}
		log.info("成功获取AccessToken:" + token);
		return token;
	}

	/**
	 * 获取小程序码,返回二进制字符流
	 */
	public static String getXcxCode1(String scene, WechatConfig config) throws Exception {
		String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken(config);
		JSONObject paramJson = new JSONObject();
		paramJson.put("scene", scene);// 参数，商家id，用户id
		String result = WechatCommonUtil.httpsRequest(url, "POST", paramJson.toString());
		log.info("获取小程序码:" + result);
		System.out.print("获取小程序码:" + result);
		if (result.indexOf("errcode") > 0) {
			return "errro";// 报错
		}
		return result;
	}

	private static final String xcxqrcode = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";

	/**
	 * 获取带参数小程序码 * http://127.0.0.1:8080/xcx/wechat/getXcxCode?scene=999浏览器访问可直接看到图片
	 * 前端展示：src="http://127.0.0.1:8080/xcx/wechat/getXcxCode?scene=999" 返回二进制流
	 */
	public static void getXcxCode(String scene, String acccess_token, String page, HttpServletResponse response) {
		response.setHeader("Pragma", "No-cache");
		response.setHeader("Cache-Control", "no-cache");
		response.setDateHeader("Expires", 0);
		response.setContentType("image/png");
		// String api =
		// "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";
		JSONObject paramJson = new JSONObject();// 参数
		paramJson.put("scene", scene);
		if (page != null && !"".equals(page))
			paramJson.put("page", page);
		BufferedInputStream bis = null;
		PrintWriter printWriter = null;
		OutputStream os = null;
		try {
			URL url = new URL(String.format(xcxqrcode, acccess_token));
			HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
			httpURLConnection.setRequestMethod("POST");// 提交模式
			httpURLConnection.setConnectTimeout(10000);// 连接超时 单位毫秒
			httpURLConnection.setReadTimeout(5000);// 读取超时 单位毫秒
			// 发送POST请求必须设置如下两行
			httpURLConnection.setDoOutput(true);
			httpURLConnection.setDoInput(true);
			// 获取URLConnection对象对应的输出流
			printWriter = new PrintWriter(httpURLConnection.getOutputStream());
			// 发送请求参数
			if (paramJson != null) {
				printWriter.write(paramJson.toString());
				// flush输出流的缓冲
				printWriter.flush();
			}

			bis = new BufferedInputStream(httpURLConnection.getInputStream());
			os = response.getOutputStream();
			int len;
			byte[] arr = new byte[1024];
			while ((len = bis.read(arr)) != -1) {
				os.write(arr, 0, len);
				os.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (printWriter != null)
				printWriter.close();
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (bis != null) {
				try {
					bis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		// return paramJson;
	}

	/**
	 * 生成小程序码并保存图片到本地
	 */
	public static String getminiqrQr(WechatConfig config) throws Exception {
		String imgUrl = "";
		String fileName = "banban.jpg";
		String filePath = "E:\\";// 图片的存储路径
		File addNew = new File(filePath);
		if (!addNew.exists()) {
			addNew.mkdirs();// 如果文件夹不存在则创建
		}
		Map<String, Object> params = new HashMap<>();
		params.put("scene", "666");// 码携带参数
		CloseableHttpClient httpClient = HttpClientBuilder.create().build();
		HttpPost httpPost = new HttpPost(
				"https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken(config));
		httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
		String body = JSON.toJSONString(params);
		StringEntity entity;
		try {
			entity = new StringEntity(body);
			entity.setContentType("image/png");
			httpPost.setEntity(entity);
			HttpResponse response1;
			response1 = httpClient.execute(httpPost);
			InputStream inputStream = response1.getEntity().getContent();
			File targetFile = new File(filePath);
			if (!targetFile.exists()) {
				targetFile.mkdirs();
			}
			FileOutputStream out = new FileOutputStream(filePath + fileName);
			byte[] buffer = new byte[8192];
			int bytesRead = 0;
			while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
				out.write(buffer, 0, bytesRead);
			}
			out.flush();
			out.close();
			imgUrl = "https://www.baidu.com/images/xcxcode/" + fileName;// 图片全路径
			return imgUrl;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return imgUrl;
	}

	/**
	 * 银行卡实名认证接口
	 * HttpUtils下载地址：https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
	 */
	/*
	 * public static JSONObject bankCardVerification(UserBankAccountEntity bank){
	 * String host = "https://yunyidata.market.alicloudapi.com"; String path =
	 * "/bankAuthenticate4"; String method = "POST"; String appcode =
	 * "80e3730ebf2b4e3ca5e5f05de1756b94"; Map<String, String> headers = new
	 * HashMap<String, String>(); //最后在header中的格式(中间是英文空格)为Authorization:APPCODE
	 * 83359fd73fe94948385f570e3c139105 headers.put("Authorization", "APPCODE " +
	 * appcode); headers.put("Content-Type",
	 * "application/x-www-form-urlencoded; charset=UTF-8"); Map<String, String>
	 * querys = new HashMap<String, String>(); Map<String, String> bodys = new
	 * HashMap<String, String>(); bodys.put("ReturnBankInfo", "YES");
	 * bodys.put("cardNo", bank.getBankCardNumber()); bodys.put("idNo",
	 * bank.getIdNumber()); bodys.put("name", bank.getBankAccountName());
	 * bodys.put("phoneNo", bank.getBankReservedPhone()); JSONObject jsStr = null;
	 * try { HttpResponse response = HttpUtils.doPost(host, path, method, headers,
	 * querys, bodys);
	 * //{"name":"班","cardNo":"卡号","idNo":"身份证号","phoneNo":"电话","respMessage":"信息匹配"
	 * ,"respCode":"0000","bankName":"中国建设银行","bankKind":"龙卡通","bankType":"借记卡",
	 * "bankCode":"CCB"} //respCode=0000为匹配，respMessage描述 String result =
	 * EntityUtils.toString(response.getEntity());
	 * System.out.println("result:"+result); jsStr = JSONObject.parseObject(result);
	 * //System.out.println("信息:"+jsStr.get("respMessage")); } catch (Exception e) {
	 * e.printStackTrace(); } return jsStr; }
	 */

	/**
	 * xcx解密用户敏感数据
	 *
	 * @param encryptedData 明文,加密数据
	 * @param iv            加密算法的初始向量
	 * @param code          小程序登录code
	 */
	public static Map xcxUserInfo(String code, String encryptedData, String iv, WechatConfig config) throws Exception {
		Map map = new HashMap();
		String codeStr = getOpenId(code, config);// {"session_key":"1+9u3GXla06hHAmTOukw==","openid":"oDdx_4mBdXLr5GjI07-mtZKrM"}
		JSONObject codeStrJson = JSONObject.parseObject(codeStr);
		String session_key = codeStrJson.getString("session_key");
		try {
			String result = AesUtil.decrypt(encryptedData, session_key, iv, "UTF-8");
			if (null != result && result.length() > 0) {
				map.put("code", 0);
				map.put("msg", "解密成功");
				JSONObject json = JSONObject.parseObject(result);
				Map userInfo = new HashMap();
				userInfo.put("openId", json.get("openId"));
				userInfo.put("nickName", json.get("nickName"));
				userInfo.put("gender", json.get("gender"));
				userInfo.put("city", json.get("city"));
				userInfo.put("province", json.get("province"));
				userInfo.put("country", json.get("country"));
				userInfo.put("avatarUrl", json.get("avatarUrl"));
				userInfo.put("unionId", json.get("unionId"));
				map.put("userInfo", userInfo);
				return map;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * xcx解密手机号
	 *
	 * @param encryptedData 明文,加密数据
	 * @param iv            加密算法的初始向量
	 * @param session_key   获取openid接口获得
	 */
	public static String xcxUserPhone(String encryptedData, String iv, String session_key) throws Exception {
		byte[] sessionKey = Base64.decodeBase64(session_key);
		byte[] encrypData = Base64.decodeBase64(encryptedData);
		byte[] ivData = Base64.decodeBase64(iv);
		AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		SecretKeySpec keySpec = new SecretKeySpec(sessionKey, "AES");
		cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
		// 解析解密后的字符串
		String result = new String(cipher.doFinal(encrypData), "UTF-8");
		return result;
	}

}
